﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
#if !WINDOWS_PHONE


#endif

namespace DARE
{
    [AttrTypeDependentSerializable(typeof(DARE.Save.DataStructures.CCapsuleModelData))]
    public class CCapsuleModel : CBasicModel
    {

    #region fields

        private int m_slices = 20;

    #endregion

    #region properties

        public override int Slices
        {
            get { return m_slices; }
            set 
            { 
                if ((value != m_slices) && (value >= 4))
                {
                    m_slices = value;
                    SetupVertices();
                    CreateVertexBuffer();
                }
            }
        }

    #endregion

    #region ctor

        public CCapsuleModel() : 
            base(1.0f, 2.0f, 0.0f)
        {
            Initialize();
        }

        public CCapsuleModel(string name) : 
            base(name, 1.0f, 2.0f, 0.0f)
        {
            Initialize();
        }

        public CCapsuleModel(float length, float diameter) : 
            base(length, diameter, diameter)
        {
            Initialize();
        }

        public CCapsuleModel(string name, float length, float diameter) : 
            base(name, length, diameter, diameter)
        {
            Initialize();
        }

        private void Initialize()
        {
            //m_cullMode = CullMode.None;
            CullMode = CullMode.None;
            SetupVertices();
            CreateVertexBuffer();
        }

        public override AModel Clone()
        {
            CCapsuleModel clone = new CCapsuleModel(m_name, m_size.X, m_size.Y);

            base.CopyPropertiesTo(clone);
            return clone;
        }

    #endregion

    #region setup

        protected void SetupVertices()
        {
            Vector3 size = m_size / 2.0f;
            float radius = size.Y;
            float length = size.X;
            int index = 0;
#if WINDOWS_PHONE
            m_vertices = new VertexPositionNormalTexture[(m_slices / 2) * m_slices * 6 + m_slices * 6];
#else
            m_vertices = new VertexPositionNormalTextureTangentBinormal[(m_slices / 2) * m_slices * 6 + m_slices * 6];
#endif
            // points definition and normal
            Vector3 bottomLeft = Vector3.Zero;
            Vector3 bottomRight = Vector3.Zero;
            Vector3 topRight = Vector3.Zero;
            Vector3 topLeft = Vector3.Zero;
            Vector3 normal = Vector3.Zero;

            // angles needed to create the vertices
            float a = 0.0f;
            float an = 0.0f;
            float b = 0.0f;
            float bn = 0.0f;

#if WINDOWS_PHONE
            // capsule creation
            for (int j = -m_slices / 4; j < m_slices / 4; ++j)
            {
                for (int i = 0; i < m_slices; ++i)
                {
                    // calculate the angles
                    a = ((float)j) * (MathHelper.TwoPi / m_slices);
                    an = ((float)j + 1) * (MathHelper.TwoPi / m_slices);
                    b = ((float)i) * (MathHelper.TwoPi / m_slices);
                    bn = ((float)i + 1) * (MathHelper.TwoPi / m_slices);

                    // calculate the points and the normal
                    bottomLeft = new Vector3(
                        radius * (float)Math.Cos(a) * (float)Math.Cos(b), 
                        radius * (float)Math.Cos(a) * (float)Math.Sin(b), 
                        radius * (float)Math.Sin(a));
                    bottomRight = new Vector3(
                        radius * (float)Math.Cos(an) * (float)Math.Cos(b), 
                        radius * (float)Math.Cos(an) * (float)Math.Sin(b), 
                        radius * (float)Math.Sin(an));
                    topRight = new Vector3(
                        radius * (float)Math.Cos(an) * (float)Math.Cos(bn), 
                        radius * (float)Math.Cos(an) * (float)Math.Sin(bn), 
                        radius * (float)Math.Sin(an));
                    topLeft = new Vector3(
                        radius * (float)Math.Cos(a) * (float)Math.Cos(bn), 
                        radius * (float)Math.Cos(a) * (float)Math.Sin(bn), 
                        radius * (float)Math.Sin(a));
                    normal = Vector3.Cross(topLeft - bottomLeft, bottomRight - bottomLeft);
                    normal.Normalize();

                    // first demi sphere
                    if (j < 0)
                    {
                        bottomLeft.Z -= length - radius;
                        bottomRight.Z -= length - radius;
                        topRight.Z -= length - radius;
                        topLeft.Z -= length - radius;

                        // extreme point
                        if (j == -m_slices / 4)
                        {
                            m_vertices[index++] = new VertexPositionNormalTexture(bottomLeft, bottomLeft + Vector3.UnitZ * (length - radius), Vector2.Zero);
                            m_vertices[index++] = new VertexPositionNormalTexture(bottomRight, bottomRight + Vector3.UnitZ * (length - radius), Vector2.Zero);
                            m_vertices[index++] = new VertexPositionNormalTexture(topRight, topRight + Vector3.UnitZ * (length - radius), Vector2.Zero);
                        }
                        // normal case
                        else
                        {
                            m_vertices[index++] = new VertexPositionNormalTexture(bottomLeft, bottomLeft + Vector3.UnitZ * (length - radius), Vector2.Zero);
                            m_vertices[index++] = new VertexPositionNormalTexture(bottomRight, bottomRight + Vector3.UnitZ * (length - radius), Vector2.Zero);
                            m_vertices[index++] = new VertexPositionNormalTexture(topRight, topRight + Vector3.UnitZ * (length - radius), Vector2.Zero);

                            m_vertices[index++] = new VertexPositionNormalTexture(bottomLeft, bottomLeft + Vector3.UnitZ * (length - radius), Vector2.Zero);
                            m_vertices[index++] = new VertexPositionNormalTexture(topRight, topRight + Vector3.UnitZ * (length - radius), Vector2.Zero);
                            m_vertices[index++] = new VertexPositionNormalTexture(topLeft, topLeft + Vector3.UnitZ * (length - radius), Vector2.Zero);
                        }
                    }
                    // second demi sphere
                    else
                    {
                        bottomLeft.Z += length - radius;
                        bottomRight.Z += length - radius;
                        topRight.Z += length - radius;
                        topLeft.Z += length - radius;

                        // extreme point
                        if (j == m_slices / 4 - 1)
                        {
                            m_vertices[index++] = new VertexPositionNormalTexture(topRight, topRight - Vector3.UnitZ * (length - radius), Vector2.Zero);
                            m_vertices[index++] = new VertexPositionNormalTexture(topLeft, topLeft - Vector3.UnitZ * (length - radius), Vector2.Zero);
                            m_vertices[index++] = new VertexPositionNormalTexture(bottomLeft, bottomLeft - Vector3.UnitZ * (length - radius), Vector2.Zero);
                        }
                        // normal case
                        else
                        {
                            m_vertices[index++] = new VertexPositionNormalTexture(bottomLeft, bottomLeft - Vector3.UnitZ * (length - radius), Vector2.Zero);
                            m_vertices[index++] = new VertexPositionNormalTexture(bottomRight, bottomRight - Vector3.UnitZ * (length - radius), Vector2.Zero);
                            m_vertices[index++] = new VertexPositionNormalTexture(topRight, topRight - Vector3.UnitZ * (length - radius), Vector2.Zero);

                            m_vertices[index++] = new VertexPositionNormalTexture(bottomLeft, bottomLeft - Vector3.UnitZ * (length - radius), Vector2.Zero);
                            m_vertices[index++] = new VertexPositionNormalTexture(topRight, topRight - Vector3.UnitZ * (length - radius), Vector2.Zero);
                            m_vertices[index++] = new VertexPositionNormalTexture(topLeft, topLeft - Vector3.UnitZ * (length - radius), Vector2.Zero);
                        }
                    }
                    // cylinder
                    if (j == 0)
                    {
                        // calculate the points and the normal
                        topRight = new Vector3(
                            radius * (float)Math.Cos(b), 
                            radius * (float)Math.Sin(b), 
                            length - radius);
                        topLeft = new Vector3(
                            radius * (float)Math.Cos(bn), 
                            radius * (float)Math.Sin(bn), 
                            length - radius);
                        bottomRight = new Vector3(
                            radius * (float)Math.Cos(b), 
                            radius * (float)Math.Sin(b), 
                            -(length - radius));
                        bottomLeft = new Vector3(
                            radius * (float)Math.Cos(bn), 
                            radius * (float)Math.Sin(bn), 
                            -(length - radius));
                        normal = Vector3.Cross(topLeft - bottomLeft, bottomRight - bottomLeft);
                        normal.Normalize();

                        // set the vertices
                        m_vertices[index++] = new VertexPositionNormalTexture(topLeft, topLeft - Vector3.UnitZ * (length - radius), Vector2.Zero);
                        m_vertices[index++] = new VertexPositionNormalTexture(bottomLeft, bottomLeft + Vector3.UnitZ * (length - radius), Vector2.Zero);
                        m_vertices[index++] = new VertexPositionNormalTexture(bottomRight, bottomRight + Vector3.UnitZ * (length - radius), Vector2.Zero);

                        m_vertices[index++] = new VertexPositionNormalTexture(topRight, topRight - Vector3.UnitZ * (length - radius), Vector2.Zero);
                        m_vertices[index++] = new VertexPositionNormalTexture(topLeft, topLeft - Vector3.UnitZ * (length - radius), Vector2.Zero);
                        m_vertices[index++] = new VertexPositionNormalTexture(bottomRight, bottomRight + Vector3.UnitZ * (length - radius), Vector2.Zero);
                    }
                }
            }
#else
             for (int j = -m_slices / 4; j < m_slices / 4; ++j)
            {
                for (int i = 0; i < m_slices; ++i)
                {
                    // calculate the angles
                    a = ((float)j) * (MathHelper.TwoPi / m_slices);
                    an = ((float)j + 1) * (MathHelper.TwoPi / m_slices);
                    b = ((float)i) * (MathHelper.TwoPi / m_slices);
                    bn = ((float)i + 1) * (MathHelper.TwoPi / m_slices);

                    // calculate the points and the normal
                    bottomLeft = new Vector3(
                        radius * (float)Math.Cos(a) * (float)Math.Cos(b), 
                        radius * (float)Math.Cos(a) * (float)Math.Sin(b), 
                        radius * (float)Math.Sin(a));
                    bottomRight = new Vector3(
                        radius * (float)Math.Cos(an) * (float)Math.Cos(b), 
                        radius * (float)Math.Cos(an) * (float)Math.Sin(b), 
                        radius * (float)Math.Sin(an));
                    topRight = new Vector3(
                        radius * (float)Math.Cos(an) * (float)Math.Cos(bn), 
                        radius * (float)Math.Cos(an) * (float)Math.Sin(bn), 
                        radius * (float)Math.Sin(an));
                    topLeft = new Vector3(
                        radius * (float)Math.Cos(a) * (float)Math.Cos(bn), 
                        radius * (float)Math.Cos(a) * (float)Math.Sin(bn), 
                        radius * (float)Math.Sin(a));
                    normal = Vector3.Cross(topLeft - bottomLeft, bottomRight - bottomLeft);
                    normal.Normalize();

                    // first demi sphere
                    if (j < 0)
                    {
                        bottomLeft.Z -= length - radius;
                        bottomRight.Z -= length - radius;
                        topRight.Z -= length - radius;
                        topLeft.Z -= length - radius;

                        // extreme point
                        if (j == -m_slices / 4)
                        {
                            m_vertices[index++] = new VertexPositionNormalTextureTangentBinormal(bottomLeft, bottomLeft + Vector3.UnitZ * (length - radius), Vector2.Zero);
                            m_vertices[index++] = new VertexPositionNormalTextureTangentBinormal(bottomRight, bottomRight + Vector3.UnitZ * (length - radius), Vector2.Zero);
                            m_vertices[index++] = new VertexPositionNormalTextureTangentBinormal(topRight, topRight + Vector3.UnitZ * (length - radius), Vector2.Zero);
                        }
                        // normal case
                        else
                        {
                            m_vertices[index++] = new VertexPositionNormalTextureTangentBinormal(bottomLeft, bottomLeft + Vector3.UnitZ * (length - radius), Vector2.Zero);
                            m_vertices[index++] = new VertexPositionNormalTextureTangentBinormal(bottomRight, bottomRight + Vector3.UnitZ * (length - radius), Vector2.Zero);
                            m_vertices[index++] = new VertexPositionNormalTextureTangentBinormal(topRight, topRight + Vector3.UnitZ * (length - radius), Vector2.Zero);

                            m_vertices[index++] = new VertexPositionNormalTextureTangentBinormal(bottomLeft, bottomLeft + Vector3.UnitZ * (length - radius), Vector2.Zero);
                            m_vertices[index++] = new VertexPositionNormalTextureTangentBinormal(topRight, topRight + Vector3.UnitZ * (length - radius), Vector2.Zero);
                            m_vertices[index++] = new VertexPositionNormalTextureTangentBinormal(topLeft, topLeft + Vector3.UnitZ * (length - radius), Vector2.Zero);
                        }
                    }
                    // second demi sphere
                    else
                    {
                        bottomLeft.Z += length - radius;
                        bottomRight.Z += length - radius;
                        topRight.Z += length - radius;
                        topLeft.Z += length - radius;

                        // extreme point
                        if (j == m_slices / 4 - 1)
                        {                             
                            m_vertices[index++] = new VertexPositionNormalTextureTangentBinormal(topRight, topRight - Vector3.UnitZ * (length - radius), Vector2.Zero);
                            m_vertices[index++] = new VertexPositionNormalTextureTangentBinormal(topLeft, topLeft - Vector3.UnitZ * (length - radius), Vector2.Zero);
                            m_vertices[index++] = new VertexPositionNormalTextureTangentBinormal(bottomLeft, bottomLeft - Vector3.UnitZ * (length - radius), Vector2.Zero);
                        }
                        // normal case
                        else
                        {
                            m_vertices[index++] = new VertexPositionNormalTextureTangentBinormal(bottomLeft, bottomLeft - Vector3.UnitZ * (length - radius), Vector2.Zero);
                            m_vertices[index++] = new VertexPositionNormalTextureTangentBinormal(bottomRight, bottomRight - Vector3.UnitZ * (length - radius), Vector2.Zero);
                            m_vertices[index++] = new VertexPositionNormalTextureTangentBinormal(topRight, topRight - Vector3.UnitZ * (length - radius), Vector2.Zero);

                            m_vertices[index++] = new VertexPositionNormalTextureTangentBinormal(bottomLeft, bottomLeft - Vector3.UnitZ * (length - radius), Vector2.Zero);
                            m_vertices[index++] = new VertexPositionNormalTextureTangentBinormal(topRight, topRight - Vector3.UnitZ * (length - radius), Vector2.Zero);
                            m_vertices[index++] = new VertexPositionNormalTextureTangentBinormal(topLeft, topLeft - Vector3.UnitZ * (length - radius), Vector2.Zero);
                        }
                    }
                    // cylinder
                    if (j == 0)
                    {
                        // calculate the points and the normal
                        topRight = new Vector3(
                            radius * (float)Math.Cos(b), 
                            radius * (float)Math.Sin(b), 
                            length - radius);
                        topLeft = new Vector3(
                            radius * (float)Math.Cos(bn), 
                            radius * (float)Math.Sin(bn), 
                            length - radius);
                        bottomRight = new Vector3(
                            radius * (float)Math.Cos(b), 
                            radius * (float)Math.Sin(b), 
                            -(length - radius));
                        bottomLeft = new Vector3(
                            radius * (float)Math.Cos(bn), 
                            radius * (float)Math.Sin(bn), 
                            -(length - radius));
                        normal = Vector3.Cross(topLeft - bottomLeft, bottomRight - bottomLeft);
                        normal.Normalize();

                        // set the vertices
                        m_vertices[index++] = new VertexPositionNormalTextureTangentBinormal(topLeft, topLeft - Vector3.UnitZ * (length - radius), Vector2.Zero);
                        m_vertices[index++] = new VertexPositionNormalTextureTangentBinormal(bottomLeft, bottomLeft + Vector3.UnitZ * (length - radius), Vector2.Zero);
                        m_vertices[index++] = new VertexPositionNormalTextureTangentBinormal(bottomRight, bottomRight + Vector3.UnitZ * (length - radius), Vector2.Zero);

                        m_vertices[index++] = new VertexPositionNormalTextureTangentBinormal(topRight, topRight - Vector3.UnitZ * (length - radius), Vector2.Zero);
                        m_vertices[index++] = new VertexPositionNormalTextureTangentBinormal(topLeft, topLeft - Vector3.UnitZ * (length - radius), Vector2.Zero);
                        m_vertices[index++] = new VertexPositionNormalTextureTangentBinormal(bottomRight, bottomRight + Vector3.UnitZ * (length - radius), Vector2.Zero);
                    }
                }
            }
#endif
            // normalize the normals
            for (int i = 0; i < m_vertices.Length; ++i)
                m_vertices[i].Normal.Normalize();

            m_orientation = Matrix.CreateRotationX(MathHelper.PiOver2);
        }

    #endregion

    }
}
